reusable xml id validation method#4584
Conversation
🦋 Changeset detectedLatest commit: 4e30d6e The changes in this PR will be included in the next version bump. This PR includes changesets to release 52 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
SummaryThe following content is AI-generated and provides a summary of the pull request: Add Reusable
|
There was a problem hiding this comment.
The PR introduces a useful validateId abstraction in @sap-ux/project-access, but has a critical correctness bug in the async path: findFilesByExtension returns file paths, not file contents, so the XML parser receives path strings instead of XML and ID collision detection never fires when appPath is used. There are also secondary issues: a dead-code assignment in the async IIFE (fileContents is always undefined there), and the synchronous-overload tests are all async/await which silently masks whether the synchronous contract is actually being exercised.
815are
left a comment
There was a problem hiding this comment.
checked code:
- code changes are easy to understand and approach to use
fast-xml-parserinstead ofxmldom/xmldom - changelog persists
- I did not test manually
Klaus-Keller
left a comment
There was a problem hiding this comment.
adding AI verdict:
Summary
Extracts a validateId function into @sap-ux/project-access to consolidate XML element ID uniqueness checking that was previously inline in @sap-ux/fe-fpm-writer.
The function supports sync (files provided directly) and async (filesystem scan via appPath) overloads, and switches from @xmldom/xmldom DOMParser to
fast-xml-parser.
Issues Found
- Critical: Async path reads file paths, not file contents (helpers.ts:481-484)
The async branch calls findFilesByExtension which returns file paths, then passes them to fsEditor.read(path) — this part is actually correct since
mem-fs-editor.read() takes a path and returns the file content. So the bot reviewer's concern about this appears to be a false positive. However, there's still a
problem: the async path is completely untested. The test suite only covers the sync overload and a trivial "no options" case. There are no tests exercising appPath
with mocked filesystem reads.
- Behavioral change: XML parser switch (helpers.ts:378-386)
The original code used @xmldom/xmldom DOMParser with getElementById(). The new code uses fast-xml-parser with a custom recursive hasElementWithId() that searches
for @_id attributes. This is a subtle behavioral difference:
- getElementById searches for XML id attributes specifically (per DOM spec).
- The new code searches for any attribute literally named id at @_id. This should be equivalent for SAPUI5 XML views, but edge cases around namespaced id attributes
(e.g., xml:id) or custom id attribute handling could differ.
- Deep import from internal path (file.ts:4)
import { findFilesByExtension } from '@sap-ux/project-access/dist/file';
This pre-existing deep import into /dist/file is not addressed by this PR. The PR already adds findFilesByExtension as an import in helpers.ts — it would be cleaner
to also re-export it from the package's public API and update the consumer, rather than keeping the fragile /dist/ path import.
- Overload complexity (helpers.ts:313-361)
Three overload signatures for what is essentially two modes (sync with files, async with appPath/nothing). The third overload (options?: undefined →
Promise) that always returns true has questionable utility — a caller with no files and no appPath always gets true, which is a no-op validation. Consider
whether this overload is actually needed or if callers should just skip calling validateId when they have nothing to validate against.
- Nested function definitions inside implementation (helpers.ts:370-470)
Five helper functions (checkElementIdAvailable, checkIdInValue, hasElementWithId, validateAgainstFiles, and the async IIFE) are all defined inside the validateId
function body. This means they are re-created on every call. For a function called in a tight loop (the generateUniqueElementId loop calls it up to 1000 times),
this is wasteful. Extract the pure helpers (hasElementWithId, checkIdInValue, checkElementIdAvailable) to module scope.
- Missing test coverage for async/appPath overload
The test suite only tests:
- Sync overload with files provided
- Trivial async case with no options (always returns true)
There are no tests for the appPath overload, which is the more complex and error-prone path involving filesystem reads, file filtering, and mem-fs-editor
integration. This is the path most likely to have bugs.
- validatedIds check placement (helpers.ts:467)
function validateAgainstFiles(files: string[]): boolean {
return (
files.every((content) => content === '' || checkElementIdAvailable(baseId, content)) &&
!validatedIds?.includes(baseId)
);
}
The validatedIds check should come first — it's O(n) at worst but avoids parsing XML entirely if the id is already in the list. Minor optimization but worth noting
for the 1000-iteration loop in the caller.
Verdict
The core idea is sound — centralizing ID validation is a good refactor. However, the PR needs work before merging:
- Add tests for the appPath async overload
- Extract inner helper functions to module scope (performance in hot loop)
- Reorder validatedIds check before XML parsing
- Consider simplifying the overload signatures
heimwege
left a comment
There was a problem hiding this comment.
The deep import mentioned by @Klaus-Keller still exists. But it was pre-existing, so not a blocker
- changeset ok
- did NOT test manually
- coverage is good
Klaus-Keller
left a comment
There was a problem hiding this comment.
Thanks @Jimmy-Joseph19,
all my comments have been addressed.
Approved from my side.
|



Add validateId method to project-access for reusability across multiple packages